`include "../include/cpu_defines.sv"
module instBuffer(
    input logic clk,
    input logic resetn,
	input logic flush_before,
    input logic flush,
	input logic front_clear_next,
	input logic reject_inst,
	input logic reject_inst2,
	input logic branch_full,
	input logic clear_discard1,
	input logic tlb_refill,
	input logic tlb_invalid,
    input logic[1:0][31:0]   inst_i,
    input logic[31:0]   	 iaddr_i,

    input logic inst_data_ok,
	input logic [1: 0] request_num,
    input logic[1:0] inst_rdata_valid,
    input logic[1:0] issue_mode,
	input logic discard_inst2,
	input logic stall, // satll信号有效时，不再流出指令，不影响写入
	input logic [2: 0] ds_index,
	input logic delay_clear,
	input logic [2: 0] delay_index,
	input logic [2: 0] front_clear_index,

	output logic ds_en,
	output logic delay_accept,
	output logic inst1_en,
	output logic inst2_en,
    output logic[`INST_BUS] inst1,
    output logic[`INST_BUS] inst2,
    output logic[`INST_BUS] iaddr1,
    output logic[`INST_BUS] iaddr2,
	output logic inst1_tlb_refill,
	output logic inst1_tlb_invalid,
	output logic inst2_tlb_refill,
	output logic inst2_tlb_invalid,
	output logic [2: 0] index1,
	output logic [2: 0] index2,
	output logic [2: 0] next_index1,
	output logic [2: 0] next_index2,
    
    output logic instBuffer_ok,
	output logic buffer_count_ok
);

typedef struct packed{
	logic tlb_refill;
	logic tlb_invalid;
	logic [31:0] inst;
	logic [31:0] iaddr;
}ibitem;
ibitem instBuffer[ `INSTBUFFER_SIZE - 1 : 0 ];
logic [`INSTBUFFER_SIZE-1: 0] buffer_en, buffer_en_mask;

logic[1: 0] inst_num, predict_num;
assign inst_num[1] = inst_rdata_valid[1] & inst_rdata_valid[0] & ~reject_inst2;
assign inst_num[0] = inst_rdata_valid[1] ^ inst_rdata_valid[0] | inst_rdata_valid[1] & inst_rdata_valid[0] & reject_inst2;

logic[$clog2(`INSTBUFFER_SIZE) - 1 : 0] head, clear_head;
logic[$clog2(`INSTBUFFER_SIZE) - 1 : 0] tail;
logic[$clog2(`INSTBUFFER_SIZE) - 1 : 0] headp1;
logic[$clog2(`INSTBUFFER_SIZE) - 1 : 0] headp2;
logic[$clog2(`INSTBUFFER_SIZE) - 1 : 0] tailp1;
assign headp1 = head + 1;
assign headp2 = head + 2;
assign tailp1 = tail + 1;
// assign clear_head = head + issue_mode[0];
// one more bit 
logic[$clog2(`INSTBUFFER_SIZE) : 0] bufferContentLen, next_buffer_count, remain_count, availiable_count, clear_count, icache_count;
logic [31: 0] iaddr4;
wire [1:0] write_num;
wire [1:0] issue_num;
wire [1:0] add_num;
logic [2: 0] front_clear_indexp1;

assign instBuffer_ok = bufferContentLen + icache_count + request_num <= 6;
assign buffer_count_ok = bufferContentLen <= 6;
assign iaddr4 = iaddr_i + 4;
assign clear_count = front_clear_index >= clear_head ? front_clear_index - clear_head : front_clear_index - clear_head + `INSTBUFFER_SIZE;

logic writeEnable;
assign writeEnable = inst_data_ok & ~reject_inst;

assign ds_en = buffer_en[ds_index] & ~((&buffer_en) & (ds_index == head));
assign delay_accept = delay_clear & buffer_en[delay_index] & ~((&buffer_en) & (delay_index == head));
assign buffer_en_mask = ((9'b1 << front_clear_index) - 1) ^ ((9'b1 << clear_head) - 1);
assign clear_head = head + (issue_num);
assign next_index1 = tail;
assign next_index2 = tailp1;

assign front_clear_indexp1 = front_clear_index + 1;
assign write_num = writeEnable ? inst_num : 0;
assign issue_num = stall ? 0 : issue_mode[0] + issue_mode[1];
assign next_buffer_count = bufferContentLen + write_num - issue_num;
assign inst1_en = buffer_en[head] && !(clear_discard1 && front_clear_index == head);
assign inst2_en = buffer_en[headp1] && !discard_inst2 && !(clear_discard1 && front_clear_index == headp1);
assign inst1 = instBuffer[head].inst;
assign iaddr1 = instBuffer[head].iaddr;
assign inst2 = instBuffer[headp1].inst;
assign iaddr2 = instBuffer[headp1].iaddr;
assign inst1_tlb_invalid = instBuffer[head].tlb_invalid;
assign inst2_tlb_invalid = instBuffer[headp1].tlb_invalid;
assign inst1_tlb_refill = instBuffer[head].tlb_refill;
assign inst2_tlb_refill = instBuffer[headp1].tlb_refill;
assign index1 = head;
assign index2 = headp1;

integer i, j;
always_ff @(posedge clk) begin
	// clear_head <= head + (delay_accept & issue_mode[0]);
    if(resetn == `RST_ENABLE || flush ) begin
        for(i = 0; i < `INSTBUFFER_SIZE; i = i + 1) begin
			instBuffer[i] <= 0;
        end
        bufferContentLen <= 0;
        head <= 0;
        tail <= 0;
		remain_count <= `INSTBUFFER_SIZE;
		buffer_en <= 0;
		icache_count <= !resetn ? 0 : request_num;
    end
	else if(front_clear_next)begin
		head <= clear_head;
		tail <= front_clear_index;
		bufferContentLen <= clear_count;
		remain_count <= `INSTBUFFER_SIZE - clear_count;
		buffer_en <= front_clear_index >= clear_head ? buffer_en_mask :  ~buffer_en_mask;
		icache_count <= request_num;
	end
    else begin
		tail <= tail + (writeEnable ? inst_num : 0);
		icache_count <= icache_count + request_num - ({2{inst_data_ok}} & (inst_rdata_valid[1] + inst_rdata_valid[0]));
		buffer_en <= (buffer_en | ({8{writeEnable & (inst_rdata_valid[0] | inst_rdata_valid[1])}} & (1 << tail)) | 
		({8{writeEnable & inst_rdata_valid[0] & inst_rdata_valid[1] & ~reject_inst2}}) & (1 << tailp1)) &
		~({8{~stall & issue_mode[0]}} & (1 << head)) & ~({8{~stall & issue_mode[1]}} & (1 << headp1));
		
		if(writeEnable) begin 
			if(inst_rdata_valid[0])begin
				instBuffer[tail].tlb_refill <= tlb_refill;
				instBuffer[tail].tlb_invalid <= tlb_invalid;
				instBuffer[tail].inst <= inst_i[0];
				instBuffer[tail].iaddr <= iaddr_i; 
				// buffer_en[tail] <= 1'b1;
				if(!reject_inst2)begin
					instBuffer[tailp1].inst <= inst_i[1];
					instBuffer[tailp1].iaddr <= iaddr4;
				end
				// buffer_en[tailp1] <= 1'b1;
			end
			else if(inst_rdata_valid[1])begin
				instBuffer[tail].tlb_refill <= tlb_refill;
				instBuffer[tail].tlb_invalid <= tlb_invalid;
				instBuffer[tail].inst <= inst_i[1];
				instBuffer[tail].iaddr <= iaddr4;
				// buffer_en[tail] <= 1'b1;
			end
		end

		if(~stall) begin
			head <= head + issue_num;
			// if(issue_mode[0])begin
			// 	buffer_en[head] <= 1'b0;
			// end
			// if(issue_mode[1])begin
			// 	buffer_en[headp1]  <= 1'b0;
			// end
		end 
		bufferContentLen <= next_buffer_count;
		remain_count <= `INSTBUFFER_SIZE - next_buffer_count;
	end
end

endmodule